package com.ruoyi.form.service.impl;

import com.mongodb.DBRef;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Sorts;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.page.MongoPage;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.form.base.DateUtils;
import com.ruoyi.form.base.Def;
import com.ruoyi.form.base.DoubleUtils;
import com.ruoyi.form.base.StringUtils;
import com.ruoyi.form.domain.AxFile;
import com.ruoyi.form.domain.AxField;
import com.ruoyi.form.domain.AxModel;
import com.ruoyi.form.service.*;
import com.ruoyi.system.service.ISysDictDataService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.util.HtmlUtils;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;

@RequiredArgsConstructor
@Service
public class DataServiceImpl implements IDataService {
    @Autowired
    private IAxFieldService axFieldService;
    @Resource
    private IMongoService<Document> mongoService;
    @Resource
    private IAxModelService modelService;
    @Resource
    private ISysDictDataService sysDictDataService;
    @Resource
    private IAxFileService axFileService;

    @Override
    public String getPropertyName(AxField field) {
        if (null == field || StringUtils.isEmpty(field.getBasicCode())) {
            return "";
        }
        String code = field.getBasicCode().toUpperCase();
        if (axFieldService.isStaticCode(code)) {
            return code.toUpperCase();
        }
        if (StringUtils.isNotBlank(field.getFormDictType())) {
            String dictId = field.getFormDictType();
            return Def.PC_ + code + Def._D_ + dictId.toUpperCase();
        } else {
            String javatype = field.getBasicJavaType();
            if ("string".equalsIgnoreCase(javatype)) {
                return Def.PS_ + code;
            } else if ("date".equalsIgnoreCase(javatype)||"datetime".equalsIgnoreCase(javatype)) {
                return Def.PD_ + code;
            } else if ("double".equalsIgnoreCase(javatype)||"int".equalsIgnoreCase(javatype)) {
                return Def.PN_ + code;
            }
        }
        return code;
    }
    @Override
    public Document findUseMongoByCode(String collection, String code) {
        List<Bson> bson = new ArrayList<>();
        // 文件id存在parentId
        bson.add(Filters.eq(Def.code, code));
        Bson sort = Sorts.orderBy(Sorts.ascending(Def.sortNum));
        List<Document> documentList = mongoService.find(collection, Filters.and(bson), sort);
        if (CollectionUtils.isEmpty(documentList)){
            return null;
        }
        return documentList.get(0);
    }
    @Override
    public Document findOneUseMongo(String _id, String collectionName) {
        if (collectionName == null) {
            return new Document();
        }
        return mongoService.findOneById(_id,Document.class,collectionName);
    }
    /**
     * 根据id更新数据
     */
    @Override
    public void updateDataUseMongo(String collection,String id, Map<String, Object> params) {
        mongoService.saveOrUpdateOne(collection, Filters.eq(Def.id, id), params);
    }

    @Override
    public MongoPage findExtendUseMongo(MongoPage mongoPage, String collection, String fieldId, String parentId) {
        List<Bson> bson = new ArrayList<>();
        bson.add(Filters.eq(Def.tplTableId, fieldId));
        // 复杂属性默认存在TPL和parentId
        Document d = new Document();
        d.put("$ref", collection);
        d.put("$id", parentId);
        bson.add(Filters.eq(Def.parentId, d));
        Bson sort = Sorts.orderBy(Sorts.ascending(Def.sortNum));
        return mongoService.find(collection,Filters.and(bson),sort,mongoPage);
    }
    /**
     * 保存数据
     */
    @Override
    public String saveDataUseMongo(String tplId, Integer version, String collection, Document doc) {
        String id = doc.getString(Def.id);
        String tid = "";
        String _id;
        if(doc.get("_id") == null){
            _id = IdUtils.simpleUUID();
        } else {
            _id = doc.get("_id").toString();
        }
        if (null != id) {
            tid = id;
        }
        AxModel table = modelService.selectAxModelById(tplId);
        if (table != null && !StringUtils.isEmpty(table.getId())) {// 表
            List<AxField> fields = axFieldService.findFields(tplId);
            Map<String, Object> params = build(fields, doc, _id, collection);
            params.put(Def.version, version);
            Document d = null;
            if (StringUtils.isNotBlank(tid)) {
                d = mongoService.findOneById(tid, Document.class, collection);
            }
            if (d != null && !d.isEmpty()) {
                // d存在 更新
                _id = tid;
                params.put(Def.updateTime, DateUtils.getDate("yyyy-MM-dd HH:mm:ss"));
                updateDataUseMongo(collection, tid, params);
            } else {
                // d不存在 新增
                if (StringUtils.isNotBlank(tid)) {
                    // tid不为空, 使用tid新增数据
                    _id = tid;
                }
                params.put(Def.id, _id);
                params.put(Def.tplTableId, tplId);
                params.put(Def.createTime, DateUtils.getDate("yyyy-MM-dd HH:mm:ss"));
                if (doc.containsKey(Def.sortNum)) {
                    params.put(Def.sortNum, doc.get(Def.sortNum));
                }
                // 层级
                if (doc.containsKey(Def.hierarchy)) {
                    params.put(Def.hierarchy, doc.getInteger(Def.hierarchy));
                }
                mongoService.insertOrUpdate(collection, new Document(params));
            }
            //  更新文件列表
            if (doc.containsKey("_selfUploadFiles")){
                try {
                    List<Map<String,Object>> fileList =  (List<Map<String,Object>>) doc.get("_selfUploadFiles");
                    for (Map<String,Object> m : fileList) {
                        AxFile file =  axFileService.selectAxFileById(m.get("fileId").toString());
                        file.setPartId(_id);
                        file.setFieldId(m.get("fieldId").toString());
                        axFileService.updateById(file);
                    }
                }catch (Exception e){
                    // 文件上传失败
                }
                doc.remove("_selfUploadFiles");
            }
            if (!doc.containsKey(Def.tplTableId)) {
                return _id;
            }
        } else {// 复杂字段
            AxField field = axFieldService.selectAxFieldById(tplId);
            List<AxField> fields = axFieldService.queryChildList(field.getId());
            Map<String, Object> params = build(fields, doc, _id, collection);
            params.put(Def.version, version);
            Document d;
            d = mongoService.findOneById(tid, Document.class, collection);
            if (StringUtils.isNotBlank(tid) && d.size() != 0) {
                params.put(Def.updateTime, DateUtils.getDate("yyyy-MM-dd HH:mm:ss"));
                updateDataUseMongo(collection, tid, params);
            } else {
                params.put(Def.createTime, DateUtils.getDate("yyyy-MM-dd HH:mm:ss"));
                params.put(Def.id, _id);
                params.put(Def.tplTableId, tplId);
                if(ObjectUtils.isNotEmpty(doc.get("parentId"))){
                    Map<String, Object> map = new HashMap<>();
                    map.put("$ref", collection);
                    map.put("$id", doc.get("parentId"));
                    params.put(Def.parentId, map);
                }
                mongoService.insertOrUpdate(collection, new Document(params));
            }
            if (doc.containsKey("_selfUploadFiles")){
                try {
                    List<Map<String,Object>> fileList =  (List<Map<String,Object>>) doc.get("_selfUploadFiles");
                    for (Map<String,Object> m:fileList) {
                        AxFile file =  axFileService.selectAxFileById(m.get("fileId").toString());
                        file.setPartId(StringUtils.isNotBlank(tid)?tid:_id);
                        file.setFieldId(m.get("fieldId").toString());
                        axFileService.updateById(file);
                    }
                } catch (Exception e) {
                    // 文件上传失败
                }
                doc.remove("_selfUploadFiles");
            }
        }
        return StringUtils.isNotBlank(tid) ? tid : _id;
    }
    @Override
    public Integer findMaxSortNum(String fieldId, String parentId) {
        Bson sort = Sorts.orderBy(Sorts.descending(Def.sortNum));
        Document d = new Document();
        d.put("$ref", Def.formSubDataCollection);
        d.put("$id", parentId);
        int a ;
        try {
            List<Bson> bson = new ArrayList<>();
            bson.add(Filters.and(Filters.eq(Def.tplTableId, fieldId),
                    Filters.eq(Def.parentId, d), Filters.eq(Def.delFlag, "0")));
            List<Document> list = mongoService.find(Def.formSubDataCollection,Filters.and( bson), sort);
            if (list.size() ==0) {
                a = 0;
            }else {
                a = (Integer)list.get(0).get(Def.sortNum);
            }
        }catch(Exception e) {
            return null;
        }
        return a;
    }
    @Override
    public String saveDataUseMongo(String collection, String tplId, Document doc) {
        return saveDataUseMongo(tplId, 1, collection, doc);
    }
    @Override
    public void delDataUseMongo(List<String> id,String collection) {
        this.mongoService.deleteByIds(id, collection);
    }
    @Override
    public AjaxResult editUpOrDown(MongoPage mongoPage, String collection, String tplId, String parentId, String id, String type) {
        List<Bson> bson = new ArrayList<>();
        bson.add(Filters.eq(Def.tplTableId, tplId));
        // 复杂属性默认存在TPL和parentId
        Document d = new Document();
        d.put("$ref", collection);
        d.put("$id", parentId);
        bson.add(Filters.eq(Def.parentId, d));
        Bson sort = Sorts.orderBy(Sorts.ascending(Def.sortNum));
        List<Document> list = this.mongoService.find(collection, Filters.and(bson), sort);
        if (list.size() == 0) {
            return AjaxResult.error("未找到数据！");
        } else {
            if (id.equals(list.get(0).get("_id"))) {
                if("UP".equals(type)){
                    return AjaxResult.error("修改失败:该数据无法向上");
                }
            }
            if (id.equals(list.get(list.size()-1).get("_id"))) {
                if ("DOWN".equals(type)) {
                    return AjaxResult.error("修改失败:该数据无法向下");
                }
            }
            if (type.equals("UP")) {
                int number = 0;
                for (Document document : list) {
                    if (id.equals(document.get("_id"))) {
                        Integer sortnum = Integer.parseInt(document.get("_SORTNUM").toString());
                        Integer sortnumUP = Integer.parseInt(list.get(number-1).get("_SORTNUM").toString());
                        document.put("_SORTNUM",sortnumUP);
                        list.get(number-1).put("_SORTNUM",sortnum);
                        Map<String, Object> map = new HashMap<>(document);
                        Map<String, Object> mapUP = new HashMap<>(list.get(number - 1));
                        this.updateDataUseMongo(collection, id, map);
                        this.updateDataUseMongo(collection, list.get(number-1).get("_id").toString(), mapUP);
                        break;
                    } else {
                        number++;
                    }
                }
                return AjaxResult.success("修改成功");
            }
            if (type.equals("DOWN")) {
                int number = 0;
                for (Document document : list) {
                    if(id.equals(document.get("_id"))){
                        Integer sortnum = Integer.parseInt(document.get("_SORTNUM").toString());
                        Integer sortnumUP = Integer.parseInt(list.get(number+1).get("_SORTNUM").toString());
                        document.put("_SORTNUM",sortnumUP);
                        list.get(number+1).put("_SORTNUM",sortnum);
                        Map<String, Object> map = new HashMap<>(document);
                        Map<String, Object> mapUP = new HashMap<>(list.get(number + 1));
                        this.updateDataUseMongo(collection, id, map);
                        this.updateDataUseMongo(collection, list.get(number+1).get("_id").toString(), mapUP);
                        break;
                    }else{
                        number++;
                    }
                }
                return AjaxResult.success("修改成功");
            }
            return AjaxResult.success("修改成功");
        }
    }
    private Map<String, Object> build(List<AxField> fields, Document doc, String newId, String collection) {
        Map<String, Object> k = new HashMap<>();
        k.put(Def.delFlag, doc.getOrDefault(Def.delFlag, "0"));
        Set<String> savedKeySet = new HashSet<>();
        for (AxField bm : fields) {
            String key = getPropertyName(bm);
            savedKeySet.add(key);
            if ("false".equalsIgnoreCase(bm.getDataExtend()) || "cite".equalsIgnoreCase(bm.getDataExtend())) {
                if (doc.containsKey(bm.getBasicCode()) && doc.get(bm.getBasicCode()) != null) {
                    String kv = doc.get(bm.getBasicCode()).toString();
                    k.put(key, convertField(bm, kv));
                    if (!StringUtils.isEmpty(bm.getFormDictType()) && k.get(key)!=null) {
                        String k_v = Def.PV_ + key.substring(Def.PC_.length());
                        String kv_v = "";
                        String[] values = k.get(key).toString().split(",");
                        for (int i = 0; i < values.length; i++) {
                            SysDictData dict=  sysDictDataService.selectDictDataById(Long.parseLong(kv));
                            if(dict != null) {
                                kv_v += "," + dict.getDictLabel();
                            }
                        }
                        if (kv_v.length() > 0) {
                            kv_v = kv_v.substring(1);
                        }
                        k.put(k_v, kv_v);
                    }
                }
            }
        }

        //放开自定义列保存
        //只额外保存下划线开头的数据
        for (String key : doc.keySet()) {
            if (!key.endsWith("_InputValue") && !"_selfUploadFiles".equals(key)){
                k.put(key, doc.get(key));
            }
        }
        // 此处主要用于复杂属性 默认存在TPL和parentid
        if (doc.containsKey(Def.tplTableId)) {
            String tpl = doc.get(Def.tplTableId).toString();
            AxModel tb = modelService.selectAxModelById(tpl);
            if (tb == null) {
                // 表示此时为复杂属性
                if (StringUtils.isNotBlank(tpl)) {
                    k.put(Def.tplTableId, tpl);
                }
                if (doc.containsKey(Def.parentId )) {
                    String id = ((String) doc.get(Def.parentId));

                    if (StringUtils.isNotBlank(id)) {
                        Map<String, Object> map = new HashMap<>();
                        map.put("$ref", collection);
                        map.put("$id", id);
                        k.put(Def.parentId, map);
                    }
                    Document d = findTopParent(id, collection);
                    if (!d.isEmpty()) {
                        k.put(Def.TableId, d.getString(Def.tplTableId));
                    }
                }
                if (doc.containsKey(Def.sortNum)) {
                    k.put(Def.sortNum, doc.getInteger(Def.sortNum));
                } else if (doc.containsKey("gridNumber")) {
                    k.put(Def.sortNum, doc.getInteger("gridNumber"));
                }

            } else {
                k.put(Def.TableId, tpl);
            }
        }
        return k;
    }
    private static Object convertField(AxField field, String... v) {
        if (v == null)
            return null;
        String t;
        if (v.length == 1) {
            t = v[0];
            String type = field.getBasicJavaType();
            if ("timestamp".equalsIgnoreCase(type)) {
                try {
                    return DateUtils.parseDate(t);
                } catch (Exception e) {
                    new Exception("转换timestamp型数据时出错！");
                }
            } else if ("datetime".equalsIgnoreCase(type)) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                try {
                    return sdf.format(DateUtils.parseDate(t));
                } catch (Exception e) {
                    new Exception("转换datetime型数据时出错！");
                }
            } else if ("date".equalsIgnoreCase(type)) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                try {
                    return sdf.format(DateUtils.parseDate(t));
                } catch (Exception e) {
                    new Exception("转换date型数据时出错！");
                }
            }
            else if ("int".equalsIgnoreCase(type)) {
                if (StringUtils.isEmpty(t)) {
                    return "";
                }
                try {
                    return StringUtils.toInteger(t);
                } catch (Exception e) {
                    new Exception("转换int型数据时出错！");
                }
            } else if ("double".equalsIgnoreCase(type)) {
                if (StringUtils.isEmpty(t)) {
                    return "";
                }
                try {
                    return DoubleUtils.parseDouble(t);
                } catch (Exception e) {
                    new Exception("转换Double型数据时出错！");
                }

            } else {
                return HtmlUtils.htmlUnescape(t == null ? "" : t);
            }
        } else {
            return StringUtils.connect(",", v);
        }
        return null;
    }
    private Document findTopParent(String pid,String collection) {
        if (StringUtils.isNotBlank(pid)) {
            Document doc = findOneUseMongo(StringUtils.toString(pid),collection);
            return findTopParent(doc,collection);
        }
        return new Document();
    }
    private Document findTopParent(Document d,String collection) {
        if (d.containsKey(Def.parentId) && StringUtils.isNotBlank(d.getString(Def.parentId))) {
            //  项目任务parentid存字符串
            String parentId;
            if (d.get(Def.parentId) instanceof  String){
                parentId = d.getString(Def.parentId);
            } else {
                DBRef p = (DBRef) d.get(Def.parentId);
                parentId = StringUtils.toString(p.getId());
            }
            Document doc = findOneUseMongo(parentId,collection);
            return findTopParent(doc,collection);
        }
        return d;
    }
}
